//! Объект
class Object {
protected:

	//! Анимация
	Animation* _animation;

public: // Материал

	//! Материал
	Material* material;

protected: // Mesh

	//! Вершина для геометрии в мировых координатах 
	//* Меш без индексов, готовый к выводу через DrawArray (VBO)
	struct WorldVertex {
		vec3 position;
		vec3 normal;
		vec2 texcoord;
	};
	
	//! Тип для массива вершин (мировых)
	typedef vector <WorldVertex> WVertexVector;
	
	//! Вершины в мировых координатах
	WVertexVector _world_vertex;
	
	//! Лево-нижне-ближняя вершина куба в который вписан меш в мировых координатах
	//* Для ускорения вычисления пересечения с мешем
	vec3 _lbn;
	
	//! Право-верхне-дальняя вершина куба в который вписан мещ
	vec3 _rtf;
	
	//! Установит флаг обновления для текущего объекта и всех вложенных объектов (тотальное обновление)
	void _set_update_flag_rec(){
		mesh._update = true;
		for (ObjectMap::iterator it = _child.begin(); it != _child.end(); ++it)
			(*it).second._set_update_flag_rec();
	}
	
	//! Обновляет объект
	void _update_object(){
		// проверка на изменение анимации
		if (_animation && _animation->_update){
			_animation->_update_final_matrix();
			_set_update_flag_rec();
		}
		// обновление меша и объектов
		if (mesh._update){
			if (mesh._face.empty()){
				// если массив граней пуст то просто чистим	
				if (!_world_vertex.empty())
					WVertexVector().swap(_world_vertex);
				_lbn = _rtf = vec3(0,0,0);
			} else {
				unsigned int sz, size;
				size = sz = mesh._face.size();
				// получаем в size число вершин
				size *= 3;
				// подготавливаем размер _world_vertex_size
				if (_world_vertex.size()!=size)
					_world_vertex.resize(size);
				// бежим по массивам указателями для ушустрения процесса
				WorldVertex* v = &_world_vertex[0];
				Mesh::Face* f = &(mesh._face)[0];
				// вычисляем матрицу для нормалей
				mat3 _normal_matrix = mat3(_final_matrix);
				// получаем число джоинтов
				unsigned short asize = _animation?_animation->final_matrix.size():0;
				// цикл по граням
				for (unsigned int i=0;i!=sz;i++){
			
					// face vertex 0
					Mesh::Vertex* vp = &mesh._vertex[(*f).vertex[0]];
					vec4 vt = vec4((*vp).position,1);
					vec3 vn = mesh._normal[(*f).normal[0]];
					if (_animation)
						if ((vp->bone>=0) && ((unsigned short)(*vp).bone<asize)){
							vt = _animation->final_matrix[(*vp).bone] * vt;
							vn = mat3(_animation->final_matrix[(*vp).bone]) * vn;
						}
					(*v).position = vec3(_final_matrix*vt);
					if (i) { 
						if ((*v).position.x < _lbn.x) _lbn.x = (*v).position.x;
						if ((*v).position.y < _lbn.y) _lbn.y = (*v).position.y;
						if ((*v).position.z < _lbn.z) _lbn.z = (*v).position.z;
						if ((*v).position.x > _rtf.x) _rtf.x = (*v).position.x;
						if ((*v).position.y > _rtf.y) _rtf.y = (*v).position.y;
						if ((*v).position.z > _rtf.z) _rtf.z = (*v).position.z;
					} else { // i==0 first vertex
						_lbn = _rtf = (*v).position;				
					}
					(*v).normal = _normal_matrix * vn;
					(*v).texcoord = mesh._texcoord[(*f).texcoord[0]];
					v++;
				
					// face vertex 1
					vp = &mesh._vertex[(*f).vertex[1]];
					vt = vec4((*vp).position,1);
					vn = mesh._normal[(*f).normal[1]];
					if (_animation)
						if ((vp->bone>=0) && ((unsigned short)(*vp).bone<asize)){
							vt = _animation->final_matrix[(*vp).bone] * vt;
							vn = mat3(_animation->final_matrix[(*vp).bone]) * vn;
						}
					(*v).position = vec3(_final_matrix*vt);
					if ((*v).position.x < _lbn.x) _lbn.x = (*v).position.x;
					if ((*v).position.y < _lbn.y) _lbn.y = (*v).position.y;
					if ((*v).position.z < _lbn.z) _lbn.z = (*v).position.z;
					if ((*v).position.x > _rtf.x) _rtf.x = (*v).position.x;
					if ((*v).position.y > _rtf.y) _rtf.y = (*v).position.y;
					if ((*v).position.z > _rtf.z) _rtf.z = (*v).position.z;
					(*v).normal = _normal_matrix * vn;
					(*v).texcoord = mesh._texcoord[(*f).texcoord[1]];
					v++;
				
					// face vertex 2
					vp = &mesh._vertex[(*f).vertex[2]];
					vt = vec4((*vp).position,1);
					vn = mesh._normal[(*f).normal[2]];
					if (_animation)
						if ((vp->bone>=0) && ((unsigned short)(*vp).bone<asize)){
							vt = _animation->final_matrix[(*vp).bone] * vt;
							vn = mat3(_animation->final_matrix[(*vp).bone]) * vn;
						}
					(*v).position = vec3(_final_matrix*vt);
					if ((*v).position.x < _lbn.x) _lbn.x = (*v).position.x;
					if ((*v).position.y < _lbn.y) _lbn.y = (*v).position.y;
					if ((*v).position.z < _lbn.z) _lbn.z = (*v).position.z;
					if ((*v).position.x > _rtf.x) _rtf.x = (*v).position.x;
					if ((*v).position.y > _rtf.y) _rtf.y = (*v).position.y;
					if ((*v).position.z > _rtf.z) _rtf.z = (*v).position.z;
					(*v).normal = _normal_matrix * vn;
					(*v).texcoord = mesh._texcoord[(*f).texcoord[2]];
					v++;
			
					f++;
				}
			}
			mesh._update = false;
		}			
	}
	
	//! Рисуем объект и предков
	void _draw(){
		if (_enable) {
			_update_object();
			if (_visible) {
				if (!_world_vertex.empty()){
					if (material)
						material->bind();
					glEnableClientState(GL_VERTEX_ARRAY);
					glVertexPointer(3,GL_FLOAT,sizeof(WorldVertex),&_world_vertex[0].position);
					glEnableClientState(GL_NORMAL_ARRAY);
					glNormalPointer(GL_FLOAT,sizeof(WorldVertex),&_world_vertex[0].normal);
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
					glTexCoordPointer(2,GL_FLOAT,sizeof(WorldVertex),&_world_vertex[0].texcoord);
					glDrawArrays(GL_TRIANGLES,0,_world_vertex.size());
					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
					glDisableClientState(GL_NORMAL_ARRAY);
					glDisableClientState(GL_VERTEX_ARRAY);					
				}
			}
			for (ObjectMap::iterator it = _child.begin(); it != _child.end(); ++it)
				(*it).second._draw();
		}
	}
	
public:
	
	//! Меш
	Mesh mesh;
	
protected: // Матрицы

	//! Матрица преобразования
	mat4 _matrix;
	
	//! Финальная матрица учитывающая предков
	mat4 _final_matrix;
	
	//! Обновит финальную матрицу и финальные матрицы потомков
	void _update_final_matrix(){
		mesh._update = true;
		if (_parent)
			_final_matrix = _parent->_final_matrix * _matrix;
		else 
			_final_matrix = _matrix;
		for (ObjectMap::iterator it = _child.begin(); it != _child.end(); ++it)
			(*it).second._update_final_matrix();
	}

public:

	//! Вернет матрицы
	const mat4& matrix() const{
		return _matrix;
	}
	
	//! Установит матрицу и обновит финальные матрицы себя и потомков
	void set_matrix(const mat4& matrix){
		_matrix = matrix;
		_update_final_matrix(); 
	}

protected: // Видимость активность

	//! Флаг видимости
	//* Отвечает только за видимость этого объкта но не предков
	bool _visible;
	
	//! Флаг включения
	//* Если false то не объект не предки не обрабатываются и не отображаются
	bool _enable;

public:

	//! Включит отображение объекта
	void show(){
		_visible = true;
	}
	
	//! Выключит отображение объекта
	void hide(){
		_visible = false;
	}
	
	//! Состояние видимости
	bool is_visible() const{
		return _visible;
	}
	
	//! Сделает объект активным
	//* также вызовет обновление объекта так как во время диактивации могли быть
	//* получены изменения но не обработанны так как объект не был активен
	void enable(){
		mesh._update = true;
		_enable = true;
	}
	
	//! Дезактивирует объект
	void disable(){
		_enable = false;
	}
	
	//! Состояние активности
	bool is_enable() const{
		return _enable;
	}
	
protected: // Дерево объектов
	
	//! Тип массива деток
	typedef map <string,Object> ObjectMap;
	
	//! Детки
	ObjectMap _child;

	//! Указатель на предка объекта
	Object* _parent;	
	
public:

	//! Даст доступ к потомку по имени (создаст если ненайдет)
	//* При создании задаст предка 
	Object& child(const string& name){
		for (ObjectMap::iterator it = _child.begin(); it != _child.end(); ++it)
			if ((*it).first == name)
				return (*it).second;
		_child[name]._parent = this;
		_child[name]._animation = _animation;
		return _child[name];
	}
	
	//! Проверка объекта на существование
	bool is_child(const string& name){
		return _child.find(name)!=_child.end();
	}
	
	//! Удалит детку
	void delete_child(const string& name){
		_child.erase(name);
	}
	
	//! Удалит все объекты
	void delete_all_child(){
		_child.clear();
	}

public:

	//! Очистка объекта
	void clear(){
		material = 0;
		mesh.clear();
		ObjectMap().swap(_child);
	}
	
public: // Конструкторы деструкторы

	Object(){
		_animation = 0;
		_parent = 0;
		_visible = true;
		_enable = true;
		_matrix = mat4(1);
		_final_matrix = mat4(1);
		material = 0;
	}
	
};
